package com.yueke.gemini.neo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.*;
import java.util.LinkedList;
import java.util.Timer;
import java.util.TimerTask;

/**
 * Neo4j连接池
 * Created by xuliugen on 2017/10/19.
 */
public class Neo4jPool {

    private static final Logger LOG = LoggerFactory.getLogger(Neo4jPool.class);

    /**
     * 最大连接数
     */
    private static final int NEO4J_CONN_MAX_LINK = 20;
    /**
     * 最小连接数
     */
    private static final int NEO4J_CONN_MIN_LINK = 5;
    /**
     * 每次增长的连接数
     */
    private static final int NEO4J_CONN_INCREASE_LINK = 5;
    /**
     * 链接超时时间
     */
    private static final int NEO4J_CONN_TIMER = 60000;

    //HTTP Driver
    private static final String NEO4J_BOLT_URL = "jdbc:neo4j:http://192.168.1.196:7474";
    private static final String NEO4J_USERNAME = "neo4j";
    private static final String NEO4J_PASSWORD = "neo4j";

    /**
     * 当前连接数
     */
    private static int connectionCurrLink = 0;

    private static LinkedList<Connection> datasourcePool = new LinkedList<>();

    static {
        try {
            Class.forName("org.neo4j.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public Neo4jPool() {
        try {
            createConnection(0);
        } catch (Exception e1) {
            LOG.error("创建连接失败", e1);
        }
        // 通过构造函数启动定时器以达到定时释放空闲连接目的
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                try {
                    // 得到空闲连接，datasourcePool里面有几个对象就表示有几个空闲连接
                    int leisureLink = Neo4jPool.datasourcePool.size();
                    System.out.println(leisureLink);
                    // 最小连接数
                    int connectionMinLink = NEO4J_CONN_MIN_LINK;
                    // 当空闲连接大于DataSourcePool设置的最小连接数时则关闭
                    if (leisureLink > connectionMinLink) {
                        for (int i = 0; i < leisureLink - connectionMinLink; i++) {
                            Neo4jPool.closeConnection(Neo4jPool.getConnection());
                            connectionCurrLink--;
                        }
                    } else {
                        System.out.println("保持最小连接,将继续保持连接池");
                    }
                } catch (NumberFormatException e) {
                    throw new NumberFormatException("设置了无效的最小连接数");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }, 0, NEO4J_CONN_TIMER);
    }

    /**
     * 创建连接
     * @param type
     * @throws Exception
     */
    private static void createConnection(int type) throws Exception {
        try {
            int link = 0;
            switch (type) {
                case 0:
                    link = NEO4J_CONN_MIN_LINK;
                    break;
                case 1:
                    // 如果当前连接+增长连接大于设定的最大连接数时，将使用最大连接数-当前连接的数量。以保持平衡
                    link = NEO4J_CONN_INCREASE_LINK;
                    int maxLink = NEO4J_CONN_MAX_LINK;
                    if (link + connectionCurrLink > maxLink) {
                        link = maxLink - connectionCurrLink;
                    }
                    break;
            }
            for (int i = 0; i < link; i++) {
                datasourcePool.addLast(DriverManager.getConnection(NEO4J_BOLT_URL, NEO4J_USERNAME, NEO4J_PASSWORD));
                connectionCurrLink++;
            }
        } catch (NumberFormatException n) {
            throw new NumberFormatException("配置连接参数有误");
        } catch (Exception e) {
            e.printStackTrace();
            throw new SQLException("超过最大连接数 ,无法创建更多连接");
        }
    }

    /**
     * 获得连接
     * @return
     * @throws Exception
     */
    public static Connection getConnection() throws Exception {
        // 取连接加锁，防止并发取的同样的连接
        synchronized (datasourcePool) {
            if (datasourcePool.size() > 0) {
                return datasourcePool.removeFirst();
            } else if (connectionCurrLink < NEO4J_CONN_MAX_LINK) {
                createConnection(1);
                return datasourcePool.removeFirst();
            }
        }
        return null;
    }

    /**
     * 关闭连接
     * @param con
     * @throws SQLException
     */
    public static void closeConnection(Connection con) throws SQLException {
        con.close();
    }

    /**
     * 释放连接
     * @param con
     */
    public void freeConnection(Connection con) {
        datasourcePool.addLast(con);
    }

    public static void main(String[] args) {
//        try {
//            Connection conn = Neo4jPool.getConnection();
//            try (Statement stmt = conn.createStatement()) {
//                ResultSet rs = stmt.executeQuery("MATCH (n) RETURN n LIMIT 1");
//                while (rs.next()) {
//                    System.out.println(rs.getString("n.name"));
//                }
//            }
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
    }

}
